Skip to content

feat: support for oidc max age#949

Merged
steveiliop56 merged 5 commits into
mainfrom
feat/oidc-max-age
Jun 19, 2026
Merged

feat: support for oidc max age#949
steveiliop56 merged 5 commits into
mainfrom
feat/oidc-max-age

Conversation

@steveiliop56

@steveiliop56 steveiliop56 commented Jun 19, 2026

Copy link
Copy Markdown
Member

Depends on #948

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced OpenID Connect authorization flow with full prompt parameter support and validation of parameter combinations.
    • Added support for max_age parameter to enforce maximum session age requirements.
  • Bug Fixes

    • Improved error handling for authentication state detection in authorization endpoints.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 31add22f-5226-460e-8996-684a1e92db7b

📥 Commits

Reviewing files that changed from the base of the PR and between 95fc108 and a801163.

📒 Files selected for processing (1)
  • internal/service/oidc_service.go

📝 Walkthrough

Walkthrough

The OIDC authorize endpoint now parses and enforces prompt and max_age semantics: it validates prompt combinations, rejects prompt=none for unauthenticated users with a login_required callback error, computes whether the existing session exceeds max_age, and propagates a new OIDCPrompt value through redirect parameters. AuthorizeRequest gains a MaxAge field, and access token minting now receives entry.AuthTime.

Changes

OIDC prompt/max_age enforcement and auth_time wiring

Layer / File(s) Summary
Service contract: MaxAge field on AuthorizeRequest
internal/service/oidc_service.go
AuthorizeRequest gains a MaxAge string field with form, json, and url struct tags to carry the max_age authorization parameter.
Controller: prompt enforcement, max_age check, OIDCPrompt redirect, auth_time wiring
internal/controller/oidc_controller.go
Adds strconv import; extends AuthorizeScreenParams with OIDCPrompt mapped to oidc_prompt; reworks /authorize to normalize prompts, reject invalid prompt=none combinations, return login_required when prompt=none and no authenticated user exists (ErrUserContextNotFound included), and override OIDCPrompt to force login when max_age is exceeded. Fixes /authorize-complete to treat ErrUserContextNotFound as unauthenticated. Updates the token endpoint to pass entry.AuthTime into GenerateAccessToken.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • tinyauthapp/tinyauth#948: Directly related — implements end-to-end prompt support and the GenerateAccessToken(authTime) signature change that this PR consumes.

Suggested labels

lgtm

Suggested reviewers

  • scottmckendry

🐰 A hop through the auth gate, prompt in paw,
max_age checked with a flick of a claw,
login_required echoes when no session is found,
auth_time now travels to tokens, safe and sound,
The rabbit stamps the ticket — all OIDC-bound! 🎟️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: support for oidc max age' clearly and directly summarizes the main change—adding max age parameter support to the OIDC authentication flow, which is confirmed by the raw summary and PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/oidc-max-age

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/controller/oidc_controller.go 0.00% 15 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/service/oidc_service.go (1)

938-949: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

max_age is not extracted from JWT claims.

DecodeAuthorizeJWT extracts prompt but not max_age. If a client sends the authorize request as a JWT (request object), the max_age claim will be ignored while req.MaxAge remains empty.

🔧 Proposed fix
 	return &AuthorizeRequest{
 		Scope:               get("scope"),
 		ResponseType:        get("response_type"),
 		ClientID:            get("client_id"),
 		RedirectURI:         get("redirect_uri"),
 		State:               get("state"),
 		Nonce:               get("nonce"),
 		CodeChallenge:       get("code_challenge"),
 		CodeChallengeMethod: get("code_challenge_method"),
 		Prompt:              get("prompt"),
+		MaxAge:              get("max_age"),
 	}, nil
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/oidc_service.go` around lines 938 - 949, The
DecodeAuthorizeJWT function is missing extraction of the max_age claim from the
JWT when constructing the AuthorizeRequest struct. Add a new field MaxAge to the
AuthorizeRequest return statement and populate it using the get() method with
the parameter "max_age", following the same pattern used for other JWT claims
like prompt, state, and nonce.
🧹 Nitpick comments (1)
internal/service/oidc_service.go (1)

676-678: ⚡ Quick win

Consider OIDC spec compliance for auth_time during refresh.

Setting auth_time to 0 emits "auth_time": 0 in the ID token (Unix epoch). Per OIDC Core spec, auth_time should reflect the original authentication time. Consider either:

  1. Persisting auth_time in the OIDC session DB and retrieving it during refresh
  2. Omitting auth_time entirely from refreshed tokens (use omitempty behavior)

The current approach could confuse relying parties that validate auth_time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/oidc_service.go` around lines 676 - 678, The generateIDToken
call in the refresh token flow is passing a hardcoded auth_time value of 0,
which violates OIDC spec compliance. Either persist the original auth_time value
when the OIDC session is first created and stored in the entry object, then
retrieve and pass that value during refresh instead of 0, OR modify the
generateIDToken function and the underlying token generation logic to support
omitting auth_time entirely from the JWT when it's not available (using
omitempty struct tags). This ensures that refreshed ID tokens either contain the
original authentication time or exclude the field entirely rather than emitting
a zero Unix timestamp.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/controller/oidc_controller.go`:
- Around line 222-242: The code accesses userContext.Authenticated and
userContext.AuthTime in the max_age validation block without checking if
userContext is nil or in a valid state. If NewFromGin returns an error other
than ErrUserContextNotFound, userContext could be nil or partially initialized,
leading to a nil dereference. Add a nil check for userContext before the
condition that checks userContext.Authenticated to ensure the userContext is
properly initialized before accessing its fields.

---

Outside diff comments:
In `@internal/service/oidc_service.go`:
- Around line 938-949: The DecodeAuthorizeJWT function is missing extraction of
the max_age claim from the JWT when constructing the AuthorizeRequest struct.
Add a new field MaxAge to the AuthorizeRequest return statement and populate it
using the get() method with the parameter "max_age", following the same pattern
used for other JWT claims like prompt, state, and nonce.

---

Nitpick comments:
In `@internal/service/oidc_service.go`:
- Around line 676-678: The generateIDToken call in the refresh token flow is
passing a hardcoded auth_time value of 0, which violates OIDC spec compliance.
Either persist the original auth_time value when the OIDC session is first
created and stored in the entry object, then retrieve and pass that value during
refresh instead of 0, OR modify the generateIDToken function and the underlying
token generation logic to support omitting auth_time entirely from the JWT when
it's not available (using omitempty struct tags). This ensures that refreshed ID
tokens either contain the original authentication time or exclude the field
entirely rather than emitting a zero Unix timestamp.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 30182146-d821-4934-bb60-ff56143d5be2

📥 Commits

Reviewing files that changed from the base of the PR and between 6ccc894 and 97eadbc.

📒 Files selected for processing (6)
  • frontend/src/lib/hooks/screen-params.ts
  • frontend/src/pages/authorize-page.tsx
  • frontend/src/pages/login-page.tsx
  • internal/controller/oidc_controller.go
  • internal/model/context.go
  • internal/service/oidc_service.go

Comment thread internal/controller/oidc_controller.go Outdated
@scottmckendry

Copy link
Copy Markdown
Member

This one LGTM too, just ping me when #948 is merged and I'll sign off.

@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jun 19, 2026
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jun 19, 2026
@steveiliop56 steveiliop56 merged commit efe3730 into main Jun 19, 2026
5 checks passed
@steveiliop56 steveiliop56 deleted the feat/oidc-max-age branch June 19, 2026 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants